EDA : EXPLORATORY DATA ANALAYSIS¶

In [81]:
#Quelques imports très utiles
import pandas as pd 
import numpy as np
import matplotlib.pyplot as plt 
import os 
import seaborn as sns
from io import BytesIO
import base64
import warnings
import plotly.express as px
import json
from datetime import datetime
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error, r2_score
import re
import folium

Table des matières:¶

  • ### I. La pollution en France

  • ### II. Répartition de la population en France métropolitaine

  • ### III. Répartition des véhicules électriques en France métropolitaine

  • ###### III.A Véhicules électriques en France
  • ###### III.B Analyse du fichier
  • ###### III.C Répartition des ventes de véhicules en fonction de leur motorisation en France
  • ###### III.D Classement des véhicules électriques en France métropolitaine
  • ###### III.E Autonomie des voitures électriques
  • ###### III.F Évolution des voitures électriques en France métropolitaine
  • ###### III.G Voitures particulières immatriculées par commune et par type de recharge
  • ###### III.H Projection de ventes de voitures électriques selon l'IEA
  • ###### III.I Tentative de prédictions

  • ### IV. Répartition des bornes électriques en France métropolitaine

  • ### V. Accidents en France métropolitaine

I. La pollution en France¶

Lien vers le dataset : https://www.insee.fr/fr/statistiques/2015759

In [2]:
URL = "https://www.insee.fr/fr/statistiques/fichier/2015759/deve-envir-emissions-co2.xlsx"
df = pd.read_excel(URL)

# Rendre le dataframe lisible
def transform(dataset):
    colonnes = ['Émissions de gaz à effet de serre par activité'] + [ f'{i}' for i in range(1990,2023)]
    dataset.set_axis(colonnes, axis=1, inplace=True)
    dataset = dataset[3:11].reset_index()
    dataset.drop('index', axis=1, inplace = True)
    return dataset
df = transform(df)
df.head()
Out[2]:
Émissions de gaz à effet de serre par activité 1990 1991 1992 1993 1994 1995 1996 1997 1998 ... 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022
0 Industrie de l’énergie 78.851202 80.089495 81.357832 69.065977 65.973570 68.502396 72.081708 67.707550 80.069139 ... 60.838441 47.198489 49.956407 53.546045 57.306330 47.982937 46.237280 41.266444 42.490917 44.575656
1 Industrie manufacturière et construction 139.412357 151.891836 139.881424 134.659609 135.640506 136.156076 138.341342 137.138262 131.172430 ... 88.703973 86.003308 83.687003 83.202542 82.886590 82.798769 79.778832 72.046643 77.952643 72.973903
2 Traitement centralisé des déchets 15.770500 16.468496 17.264753 18.042975 18.419108 18.651170 18.695998 18.681612 19.112439 ... 16.672992 16.007408 14.990339 15.011350 15.092677 14.821750 16.019293 15.927553 15.208793 15.177475
3 Usage des bâtiments et activités résidentiels/... 93.260689 102.932198 99.120231 95.513850 89.059078 88.948882 98.907077 94.048578 98.784997 ... 98.034366 81.848655 84.846037 84.777680 84.062820 79.047162 76.038243 71.317465 75.090141 64.024935
4 Agriculture/sylviculture 88.297176 87.390855 87.067203 85.916454 85.034512 85.708999 86.605312 86.666545 86.890664 ... 81.963010 83.494725 83.110580 81.690808 81.314373 80.438064 78.615927 78.244410 76.506548 76.524915

5 rows × 34 columns

In [10]:
# Tracer l'évolution 
plt.figure(figsize=(12,8))
sns.set(style="whitegrid")
palette = sns.color_palette("husl", n_colors=7)
colonnes = df.columns.to_list()[1:]
colonnes_int = [int(x) for x in colonnes]

for i in df.index.to_list():
    if i<7:
        nom = df['Émissions de gaz à effet de serre par activité'][i]
        evol = [df[col][i] for col in colonnes]
        #evol = df[colonnes][i:i+1].values
        plt.plot(colonnes_int, evol, label = f'{nom}', color = palette[i])
    else:
        pass
plt.xlabel('Année')
plt.ylabel('en millions de tonnes d’équivalent CO₂')
plt.title('Évolution des émissions de gaz à effet de serre par activité')
plt.legend(bbox_to_anchor=(1.05, 1), loc='upper left')
plt.show()
img_data = BytesIO()  # Conversion du graphique en image base64
plt.savefig(img_data, format='png')
img_data.seek(0)
img_base64 = base64.b64encode(img_data.read()).decode()
graph_html = f'<img src="data:image/png;base64,{img_base64}" alt="Graphique d\'autonomie">'  #conserver le graphique sous format html pour le site
<Figure size 640x480 with 0 Axes>

Les facteurs d’émissions des différents modes de transport routier en Europe¶

Lien vers le site : https://www.data.gouv.fr/fr/datasets/etude-facteurs-demissions-des-differents-modes-de-transport-routier/

Ce jeu de données est issu de la base de données européenne HBEFA v4.1 (Handbook of emissions factors for Road Transport) qui fournit des facteurs d’émissions, c’est-à-dire des émissions de polluants en g/km pour toutes les catégories de véhicules routiers (voiture particulière, véhicule utilitaire léger, véhicule lourd et deux-roues motorisé), chacune étant divisée en plusieurs sous-catégories, pour une grande variété de situations de trafic (type de voirie, vitesse limite et niveau de saturation du trafic associé). Les données extraites sont des facteurs d’émissions agrégés de CO, HC, NOx et particules par catégorie de véhicules (voiture, véhicule utilitaire léger, poids lourd, autocar, bus et deux roues motorisées), par carburant (essence, gazole et GNV uniquement pour les bus), pour des conditions de circulation moyennes (urbain, rural et mixte France) et pour chaque année allant de 2020 à 2030.

In [11]:
URL = 'https://www.data.gouv.fr/fr/datasets/r/3e00d056-13ef-4069-b7bb-045859750a90'
df = pd.read_csv(URL)
In [12]:
print(df['Véhicule'].unique())
print(df.columns.to_list())
['2RM' 'PL' 'Autocar' 'Bus' 'Voiture' 'VUL']
['Année', 'Véhicule', 'Carburant', 'Roulage', 'CO (g/km)', 'HC (g/km)', 'NOx (g/km)', 'Particules (g/km)']
In [13]:
liste_vehicule = ['2RM', 'PL', 'Autocar', 'Bus', 'Voiture', 'VUL']
liste_df = [df[df['Véhicule']==f'{vehicule}'] for vehicule in liste_vehicule]

for i, dataframe in enumerate(liste_df): 
    palette = sns.color_palette("husl", n_colors=6)
    dataframe_urbain = dataframe[dataframe['Roulage']=='Urbain']
    dataframe_rural = dataframe[dataframe['Roulage']=='Rural']
    plt.figure(figsize=(12,8))
    plt.plot(dataframe_urbain['Année'], dataframe_urbain['CO (g/km)'], color = palette[0], 
             label = f"émissions de CO (g/km) pour les {liste_vehicule[i]} en zone urbaine")
    plt.plot(dataframe_rural['Année'], dataframe_rural['CO (g/km)'], color = palette[1], 
             label = f"émissions de CO (g/km) pour les {liste_vehicule[i]} en zone rurale")
    plt.plot(dataframe_urbain['Année'], dataframe_urbain['HC (g/km)'], color = palette[2], 
             label = f"émissions de HC (g/km) pour les {liste_vehicule[i]} en zone urbaine")
    plt.plot(dataframe_rural['Année'], dataframe_rural['HC (g/km)'], color = palette[3], 
             label = f"émissions de HC (g/km) pour les {liste_vehicule[i]} en zone rurale")
    plt.plot(dataframe_urbain['Année'], dataframe_urbain['NOx (g/km)'], color = palette[4], 
             label = f"émissions de NOx (g/km) pour les {liste_vehicule[i]} en zone urbaine")
    plt.plot(dataframe_rural['Année'], dataframe_rural['NOx (g/km)'], color = palette[5], 
             label = f"émissions de NOx (g/km) pour les {liste_vehicule[i]} en zone rurale")
    plt.legend(bbox_to_anchor=(1.05, 1), loc='upper left')
    plt.xlabel('Année')
    plt.show()

II. Répartition de la population en France métropolitaine¶

In [ ]:
#télécharger le fichier geojson
os.chdir("/Users/augustincablant/Documents/GitHub/Pycar")
file = ur.urlopen("https://raw.githubusercontent.com/gregoiredavid/france-geojson/master/departements-avec-outre-mer.geojson")
a = open ("DOWNLOAD/geojsonFRANCE.geojson", "w+")
for line in file :
    a.write(str(line, encoding = "UTF-8"))
a.close()
In [ ]:
#télécharger le fichier wikipedia
data = pd.read_html("https://fr.wikipedia.org/wiki/Liste_des_d%C3%A9partements_fran%C3%A7ais_class%C3%A9s_par_population_et_superficie")
file = data[0].to_csv("DOWNLOAD/PopDep.csv")
In [4]:
#On nettoie le dataframe pour le rendre lisible
os.chdir("/Users/augustincablant/Documents/GitHub/Pycar")
def clean_dataframe() :
    warnings.filterwarnings('ignore')
    dataframe = pd.read_csv("DOWNLOAD/PopDep.csv", low_memory= False, delimiter = ",", encoding="UTF-8")
    
    # Retirer les lignes du dataframe incompatible avec le fichier geojson
    dataframe2 = dataframe.loc[dataframe["CodeInsee"].apply(lambda x : len(x)) < 3]
    
    # Créer une colonne où chaque ligne représente un indice(float) de densité de population
    dataframe2["Echelle de densité"]= dataframe2["Densité(hab./km2)"].apply(lambda x : np.log(float(x.replace(",",".").replace("\xa0",""))))
    return dataframe2

#Affiche une carte de la France avec la densité de population par département
def create_choropleth_map():
    #charge le fichier sous un format adéquat
    geojsonf = json.load(open ("DOWNLOAD/geojsonFRANCE.geojson", "r"))
    #crée une carte de la France coloriée par département par indice de densité
    fig = px.choropleth(clean_dataframe(), locations = "CodeInsee", geojson = geojsonf, featureidkey= "properties.code", color = "Echelle de densité", hover_name="Département",hover_data=["Densité(hab./km2)"],title = 'Densité de population par département en France métropolitaine')
    fig.update_geos(fitbounds = "locations", visible = False)
    return fig
In [9]:
clean_dataframe()
create_choropleth_map()

III. Répartition des véhicules électriques en France métropolitaine¶

A. Véhicules électriques en France¶

Premiers pas avec le fichier¶

Le fichier est trop volumineux pour pouvoir le stocker sur le github. C'est pourquoi nous opérons quelques modifications.

In [ ]:
df = pd.read_csv('/Users/augustincablant/Desktop/data.csv')
df.head()
In [ ]:
df.shape
In [ ]:
# On ne conserve que la France 
df_fr = df[df['Country']=='FR']
df_fr.shape
In [ ]:
# On opère à une sélection des colonnes 
colonnes_utiles = ['Mp','m (kg)', 'Ft', 'ep (KW)', 'z (Wh/km)','Status', 'year', 'Fuel consumption ', 'Electric range (km)']
df_fr = df_fr[colonnes_utiles]
print(df_fr.shape)
df_fr.to_csv('/Users/augustincablant/Documents/GitHub/Pycar/DOWNLOAD/voitures_fr.csv')

B. Analyse du fichier¶

In [12]:
os.chdir("/Users/augustincablant/Documents/GitHub/Pycar")
df = pd.read_csv('DOWNLOAD/voitures_fr.csv')
df.drop('Unnamed: 0', axis=1, inplace = True)
In [13]:
df.sample(10)
Out[13]:
Mp m (kg) Ft ep (KW) z (Wh/km) Status year Fuel consumption Electric range (km)
15608 STELLANTIS 1530.0 ELECTRIC 100.0 159.0 P 2022 NaN 351.0
197444 RENAULT-NISSAN-MITSUBISHI 1201.0 ELECTRIC 31.0 161.0 P 2022 NaN 188.0
182778 RENAULT-NISSAN-MITSUBISHI 1711.0 ELECTRIC 55.0 162.0 P 2022 NaN 435.0
207503 RENAULT-NISSAN-MITSUBISHI 1201.0 ELECTRIC 31.0 161.0 P 2022 NaN 187.0
137817 TESLA-HONDA-JLR 1835.0 ELECTRIC 88.0 144.0 P 2022 NaN 491.0
191624 RENAULT-NISSAN-MITSUBISHI 1577.0 ELECTRIC 51.0 173.0 P 2022 NaN 395.0
20326 STELLANTIS 1530.0 ELECTRIC 100.0 160.0 P 2022 NaN 350.0
20700 STELLANTIS 1623.0 ELECTRIC 100.0 159.0 P 2022 NaN 341.0
42524 STELLANTIS 1659.0 ELECTRIC 57.0 153.0 P 2022 NaN 353.0
105023 RENAULT-NISSAN-MITSUBISHI 1012.0 ELECTRIC 18.0 139.0 P 2022 NaN 230.0
In [14]:
list(df.columns)
Out[14]:
['Mp',
 'm (kg)',
 'Ft',
 'ep (KW)',
 'z (Wh/km)',
 'Status',
 'year',
 'Fuel consumption ',
 'Electric range (km)']
In [15]:
list(df['Ft'].unique())
Out[15]:
['ELECTRIC', 'PETROL/ELECTRIC', 'DIESEL/ELECTRIC', 'HYDROGEN']
In [16]:
list(df['Mp'].unique())
Out[16]:
['RENAULT-NISSAN-MITSUBISHI',
 'STELLANTIS',
 'VOLKSWAGEN',
 'TESLA-HONDA-JLR',
 nan,
 'MAZDA-SUBARU-SUZUKI-TOYOTA',
 'BMW',
 'FORD',
 'MERCEDES-BENZ',
 'HYUNDAI MOTOR EUROPE',
 'KIA']
In [17]:
# Autonomie des véhicules électriques en fonction de leur poids 
plt.figure(figsize=(12,8))
sns.scatterplot(data=df, x='m (kg)', y = 'Electric range (km)', hue = 'Ft')
plt.xlabel('Poids en kg')
plt.ylabel('Autonomie en km')
plt.legend()
plt.title('Autonomie des véhicules électriques en fonction de leur poids')
plt.show()
In [18]:
# Autonomie des véhicules électriques en fonction de leur poids selon le constructeur
plt.figure(figsize=(12,8))
sns.scatterplot(data=df, x='m (kg)', y = 'Electric range (km)', hue = 'Mp')
plt.xlabel('Poids en kg')
plt.ylabel('Autonomie en km')
plt.title('Autonomie des véhicules électriques en fonction de leur poids selon le constructeur')
plt.legend()
plt.show()
In [19]:
plt.figure(figsize=(12,8))
sns.scatterplot(data=df, x='z (Wh/km)', y = 'Electric range (km)', hue = 'Mp')
plt.xlabel('Poids en kg')
plt.ylabel('Autonomie en km')
plt.legend()
plt.show()
In [20]:
# Comparons l'autonomie des véhicules 
plt.figure(figsize=(12,8))
sns.histplot(data=df, x='Electric range (km)', bins = 200, label = 'Autonomie des véhicules électriques en France')
plt.legend()
plt.show()

C. Répartition des ventes de véhicules en fonction de leur motorisation en France¶

In [23]:
os.chdir("/Users/augustincablant/Documents/GitHub/Pycar")
data_vehicles_2013_2021 = pd.read_csv('DOWNLOAD/data_vehicles_french_2013_2021.csv', low_memory=False).fillna(0)
data_vehicles_2013_2021.head()
Out[23]:
ID Country VFN Mp Mh Man MMS Tan T Va ... IT Ernedc (g/km) Erwltp (g/km) De Vf Status year Date of registration Fuel consumption Electric range (km)
0 256235 GR 0 POOL RENAULT RENAULT RENAULT SAS RENAULT e2*2001/116*0327*37 R BR1S ... 0 0.0 0.0 0.0 0.0 F 2013 0 0.0 0.0
1 256364 GR 0 POOL RENAULT RENAULT RENAULT SAS RENAULT e2*2001/116*0327*54 R 2R40 ... 0 0.0 0.0 0.0 0.0 F 2013 0 0.0 0.0
2 256433 GR 0 POOL RENAULT RENAULT RENAULT SAS RENAULT e2*2001/116*0359*29 N CNJ1 ... 0 0.0 0.0 0.0 0.0 F 2013 0 0.0 0.0
3 256451 GR 0 POOL RENAULT RENAULT RENAULT SAS RENAULT e2*2001/116*0364*22 W KW25 ... 0 0.0 0.0 0.0 0.0 F 2013 0 0.0 0.0
4 256453 GR 0 POOL RENAULT RENAULT RENAULT SAS RENAULT e2*2001/116*0373*25 Z BZ1V ... 0 0.0 0.0 0.0 0.0 F 2013 0 0.0 0.0

5 rows × 38 columns

In [24]:
repartitionvehiculesneufs = pd.read_excel('DOWNLOAD/sect-ind-auto-immat-energie.xlsx',header=3,)
repartitionvehiculesneufs = repartitionvehiculesneufs.loc[[0,1,2,3,4,5],:]
repartitionvehiculesneufs_2015_2021=repartitionvehiculesneufs[["Type d'énergie",2015,2021]]
repartitionvehiculesneufs_2015_2021.head()
Out[24]:
Type d'énergie 2015 2021
0 Essence 739.374 669.928
1 Diesel 1097.122 349.479
2 Hybride1 61.617 430.899
3 Électricité 17.268 162.106
4 Bicarburation (essence + GPL + GNV) 1.553 46.422
In [25]:
#Répartition de vente des véhicules en fonction de leur motorisation
year=[2015,2021]
for i in year:
    seuil_importance = 1  
    plt.figure(figsize=(8,8))
    data=repartitionvehiculesneufs[["Type d'énergie",i]]
    plt.pie( data.loc[:,i], 
            labels= data.loc[:,"Type d'énergie"], 
            autopct=lambda p: '{:.1f}%'.format(p) if p > seuil_importance else '',
            startangle=90, 
            labeldistance=None)
    val_legende=data.loc[:,i]/(sum(data.loc[:,i]))
    plt.legend(bbox_to_anchor=(1.05, 1))
    plt.title('Répartition de vente des véhicules en fonction de leur motorisation en {}'.format(i))
    plt.show()
In [26]:
#on garde seulement les colonnes qui nous intéressent
data_vehicles_final=data_vehicles_2013_2021.loc[:,['Mh','Cn','year','Ft','m (kg)','Enedc (g/km)','Ewltp (g/km)','Electric range (km)','Fuel consumption ']].drop_duplicates(subset='Cn',keep='first').sort_values('year',ascending=True) 
#on reshape les data (pour uniformiser les catégories)
data_vehicles_final = data_vehicles_final[data_vehicles_final['Mh']!='PSA']
data_vehicles_final = data_vehicles_final[data_vehicles_final['Ft']!=0]
data_vehicles_final['Ft']=data_vehicles_final['Ft'].apply(lambda x : x.lower())

data_vehicles_final.head(-10)
Out[26]:
Mh Cn year Ft m (kg) Enedc (g/km) Ewltp (g/km) Electric range (km) Fuel consumption
0 RENAULT CLIO 2013 petrol 1062.0 135.0 0.0 0.0 0.0
947 AUTOMOBILES CITROEN DS4 / 1.6 / HDI AUT. 2013 diesel 1375.0 114.0 0.0 0.0 0.0
946 AUTOMOBILES CITROEN DS4 / 1.6 / HDI 2013 diesel 1370.0 113.0 0.0 0.0 0.0
945 AUTOMOBILES CITROEN DS3 RACING / 1.6 / 16V TURBO 2013 petrol 1240.0 149.0 0.0 0.0 0.0
944 AUTOMOBILES CITROEN DS3 / 1.6 / 16V TURBO 2013 petrol 1165.0 135.0 0.0 0.0 0.0
... ... ... ... ... ... ... ... ... ...
1593 AUTOMOBILES CITROEN JUMPY SPACE TOURER / 2.0 / HDI L2 2021 diesel 1793.0 0.0 183.0 0.0 7.0
1592 AUTOMOBILES CITROEN JUMPY SPACE TOURER / 2.0 / AUT.HDI L2 2021 diesel 1839.0 0.0 186.0 0.0 7.1
1591 AUTOMOBILES CITROEN JUMPY SPACE TOURER / 1.5 / HDI L2 2021 diesel 1734.0 0.0 167.0 0.0 6.4
1584 AUTOMOBILES CITROEN C4 SPACETOURER / 2.0 / HDI AUT. 2021 diesel 1615.0 0.0 160.0 0.0 6.1
1598 AUTOMOBILES PEUGEOT EXPERT TRAVELLER / 2.0 / AUT. HDI L3 2021 diesel 1842.0 0.0 186.0 0.0 7.1

1662 rows × 9 columns

In [27]:
data_vehicles_constr = data_vehicles_final.loc[:,['Mh','year','Enedc (g/km)', 'Ewltp (g/km)']].groupby(['year','Mh'])
data_emission_mean = data_vehicles_constr.mean() #on construit le dataframe de la valeur moyenne des émissions
constr=list(set(data_vehicles_final['Mh'])) #on récupère la liste des constructeurs
year=[2013,2021]
for i in range(len(year)):
    data_emission=data_emission_mean.loc[data_emission_mean.index[[3*i,3*i+1,3*i+2]]]
    if i==0:
        values='Enedc (g/km)' #type de controle qui a changé
    elif i==1:
        values = 'Ewltp (g/km)' #type de controle qui a changé
    plt.figure(figsize=(12,8))
    plt.bar(constr, data_emission[values])
    plt.legend(bbox_to_anchor=(1.05, 1))
    plt.show()
No artists with labels found to put in legend.  Note that artists whose label start with an underscore are ignored when legend() is called with no argument.
No artists with labels found to put in legend.  Note that artists whose label start with an underscore are ignored when legend() is called with no argument.
In [28]:
#affichage de la répartition des véhicules neufs dévéloppés par les constructeurs français

year=[2013,2021]
for i in range(len(year)):
    data=data_vehicles_final[data_vehicles_final["year"]==year[i]]
    compt_ft=data['Ft'].value_counts()
    plt.figure(figsize=(8,8))
    plt.pie(compt_ft.values, 
            labels=compt_ft.index, 
            autopct=lambda p: '{:.1f}%'.format(p) if p > seuil_importance else '',
            startangle=90,
            labeldistance=None)
    plt.legend(bbox_to_anchor=(1.05, 1))
    plt.title('Répartition des véhicules neufs en fonction de leur motorisation en {}'.format(year[i]))
    plt.show()

D. Classement des véhicules électriques en France métropolitaine¶

In [29]:
# classement des voitures électriques en 2023
os.chdir("/Users/augustincablant/Documents/GitHub/Pycar")
classement = pd.read_csv('SCRAP/classement_VE_2023.csv')
classement.sort_values(by = 'Classement', inplace = True)
plt.figure(figsize = (16,8))
sns.barplot(x='Voitures', y='Classement', data=classement, palette='viridis')
plt.title('Classement des voitures électriques en 2023')
plt.xlabel('Voitures')
plt.ylabel('Classement')
plt.xticks(rotation=90)
plt.legend()
plt.show()

# classement.head()
No artists with labels found to put in legend.  Note that artists whose label start with an underscore are ignored when legend() is called with no argument.
In [30]:
# Faire apparaître le top 10 
plt.figure(figsize=(12, 4))
sns.barplot(x='Voitures', y='Classement', data=classement[0:10], palette='viridis')
plt.title('Classement des 10 premières voitures électriques en 2023')
plt.xlabel('Classement')
plt.ylabel('Voitures')
plt.xticks(rotation = 90)
plt.show()

E. Autonomie des voitures électriques¶

In [32]:
autonomie = pd.read_csv('SCRAP/autonomie_VE.csv')
autonomie.drop_duplicates(subset = 'Voitures', inplace = True)
autonomie.head()
Out[32]:
Classement Voitures kWh Prix Autonomie
0 1.0 Mercedes EQS 108.0 135 850 € 783 km
1 2.0 Fisker Ocean 106.0 69 950 € 707 km
3 4.0 Volkswagen ID.7 86.0 NaN 700 km
4 4.0 Peugeot e-3008 98.0 NaN 700 km
6 7.0 Tesla Model 3 76.0 50 990 € 678 km
In [33]:
def get_km(row):
    if not pd.isna(row):
        return int(row.split(' ')[0])
    else:
        return row
autonomie['Autonomie_int'] = autonomie['Autonomie'].apply(get_km)
autonomie.sort_values(by = 'Autonomie_int', ascending = False, inplace = True)

plt.figure(figsize=(16, 8))
sns.barplot(x='Voitures', y='Autonomie_int', data=autonomie[0:10], palette='viridis')
plt.title('Classement des 10 premières voitures électriques les plus autonomes en 2023')
plt.xlabel('Voitures')
plt.ylabel('Autonomie en km')
plt.xticks(rotation = 90)
plt.show()

F. Évolution des voitures électriques en France métropolitaine¶

In [36]:
os.chdir("/Users/augustincablant/Documents/GitHub/PyCar")
def evolution_nbre_voiture_elec():
    df = pd.read_csv('DOWNLOAD/Voitures.csv', sep=';')
    List_date = []
    List_nombre = []

    for row in df.itertuples():
        date0 = row.date_arrete
        date = datetime.strptime(date0, "%d/%m/%Y").strftime("%Y-%m-%d")
        nbre = row.nb_vp_rechargeables_el
        if date in List_date:
            for i in range(len(List_date)):
                if List_date[i] == date:
                    List_nombre[i] += nbre
        else:
            List_date.append(date)
            List_nombre.append(nbre)

    dict = {'Date': List_date, 'Nombre': List_nombre}

    dataframe = pd.DataFrame(dict)
    df0 = dataframe.sort_values('Date')
    fig = px.line(df0, x='Date', y='Nombre',
                  title='Evolution du parc de véhicules électriques en France')
    return fig
evolution_nbre_voiture_elec()

G. Voitures particulières immatriculées par commune et par type de recharge¶

Ce jeu de données permet de visualiser l’évolution trimestrielle (à partir de de T4 2020) du stock de voitures immatriculées dans chaque commune de France métropolitaine et DOM.

In [37]:
URL = 'https://www.data.gouv.fr/fr/datasets/r/4e4fccdb-6acb-4e31-8b2d-cb170f639f1a'
df = pd.read_csv(URL, sep = ';')
print(df.shape)
print(list(df.columns))
(422164, 8)
['codgeo', 'libgeo', 'epci', 'libepci', 'date_arrete', 'nb_vp_rechargeables_el', 'nb_vp_rechargeables_gaz', 'nb_vp']
In [38]:
df.sample(5)
Out[38]:
codgeo libgeo epci libepci date_arrete nb_vp_rechargeables_el nb_vp_rechargeables_gaz nb_vp
395142 65406 SARNIGUET 200069300.0 CA Tarbes-Lourdes-Pyrénées 2021-03-31 2 0 318
328867 53011 ASTILLÉ 200048551 CC du Pays de Craon 2020-12-31 5 0 808
330436 54173 DROUVILLE 245400759 CC du Pays du Sanon 2021-09-30 0 0 182
272824 77380 PUISIEUX 247700065 CC du Pays de l'Ourcq 2021-03-31 4 0 397
93121 12095 ESCANDOLIÈRES 241200625 CC du Pays Rignacois 2023-06-30 5 0 259
In [39]:
# Récupérer les départements 
def create_departement(dataframe):
    dataframe['Dep'] = dataframe['codgeo'].str.slice(0, 2)
    return dataframe

df = create_departement(df)
In [40]:
df.groupby("Dep")["nb_vp_rechargeables_el"].sum().reset_index()
Out[40]:
Dep nb_vp_rechargeables_el
0 01 35994
1 02 53269
2 03 34011
3 04 24126
4 05 18994
... ... ...
75 92 160815
76 93 60607
77 94 61384
78 95 53672
79 97 54954

80 rows × 2 columns

In [41]:
def new_dataframeS(dataframe):
    colonnes = ['Dep', 'nb_vp_rechargeables_el','nb_vp']
    dataframe = dataframe[colonnes]
    dataframe_elec = dataframe.groupby("Dep")["nb_vp_rechargeables_el"].sum().reset_index()
    dataframe_total = dataframe.groupby("Dep")["nb_vp"].sum().reset_index()
    return dataframe_elec, dataframe_total

df_elec, df_total = new_dataframeS(df)
In [42]:
#Affiche une carte de la France avec le nombre de voitures par département
os.chdir("/Users/augustincablant/Documents/GitHub/Pycar")
def create_choropleth_map(dataframe, titre, colonne):
    #charge le fichier sous un format adéquat
    geojsonf = json.load(open ("DOWNLOAD/geojsonFRANCE.geojson", "r"))
    #crée une carte de la France coloriée par département par indice de densité
    fig = px.choropleth(dataframe, locations = "Dep", geojson = geojsonf, featureidkey= "properties.code", color = colonne, 
                        hover_data= [colonne], title = titre)
    fig.update_geos(fitbounds = "locations", visible = True)
    return fig
In [43]:
create_choropleth_map(df_elec, "Nombre de voitures électriques par département en France Métropolitaine", 'nb_vp_rechargeables_el')
In [44]:
create_choropleth_map(df_total, "Nombre de voitures par département en France Métropolitaine", 'nb_vp')

H. Projection de ventes de voitures électriques selon l'IEA¶

Source : https://www.iea.org/data-and-statistics/data-tools/global-ev-data-explorer

In [21]:
os.chdir("/Users/augustincablant/Documents/GitHub/Pycar")
df = pd.read_csv('DOWNLOAD/IEA-EV-dataEV salesProjection-STEPSCars.csv')
df.head()
Out[21]:
region category parameter mode powertrain year unit value
0 China Projection-STEPS Oil displacement Mbd Cars EV 2020 Milion barrels per day 0.046
1 China Projection-STEPS Oil displacement, million lge Cars EV 2020 Oil displacement, million lge 2800.000
2 China Projection-STEPS EV stock share Cars EV 2020 percent 1.800
3 China Projection-STEPS Electricity demand Cars EV 2020 GWh 13000.000
4 China Projection-STEPS EV sales share Cars EV 2020 percent 5.800
In [31]:
for region in list(df['region'].unique()):
    years = [2020, 2021, 2022, 2025, 2030]
    colors = plt.cm.viridis(np.linspace(0, 1, len(years)))
    IEA = "Agence Internationale de l'Energie"
    sub_df1 = df[df['region'] == region]
    sub_df = sub_df1[sub_df1['parameter'] == 'EV sales']
    years = sub_df['year'].to_list()
    values = sub_df['value'].to_list()
    plt.figure(figsize=(12,8))
    plt.bar(years, values, color = colors)
    plt.title(f"Prévision de ventes de véhicules électriques dans la région {region} selon l'{IEA}")
    plt.grid(True)
    plt.xlabel('Années')
    plt.ylabel("Nombre de véhicules électriques (en millions)")
    plt.show()

I. Tentative de prédictions¶

Objectif : prédire le nombre de bornes de recharges dans le futur

In [46]:
URL_DATASET = 'https://data.enedis.fr/api/explore/v2.1/catalog/datasets/nombre-total-de-points-de-charge/exports/csv?lang=fr&timezone=Europe%2FBerlin&use_labels=true&delimiter=%3B'
df = pd.read_csv(URL_DATASET, sep = ';')
df.sample(5)
Out[46]:
Trimestre Ouvert au public Particulier Société
8 2018 T3 24319 97164 96616
33 2017 T2 19750 68509 68930
6 2020 T2 32648 192823 180499
14 2016 T2 13861 48412 52015
20 2015 T1 8478 25638 34746
In [47]:
df.shape
Out[47]:
(35, 4)
In [48]:
def transform_col1(row):
    year = row.split(' ')[0]
    return year

def transform_col2(row):
    sem = row.split('T')[1]
    return sem


df['Semestre'] = df['Trimestre'].apply(transform_col2)
df['Année'] = df['Trimestre'].apply(transform_col1)

def transform_col3(row): 
    row = row.split('T')
    year = row[0]
    sem_nb = row[1]
    return year + 'Q' + sem_nb
df['Trimestre'] = df['Trimestre'].apply(transform_col3)
df['Trimestre'] = pd.to_datetime(df['Trimestre'], format='%Y Q%m')
df.head()
Out[48]:
Trimestre Ouvert au public Particulier Société Semestre Année
0 2023-03-01 109856 906942 652131 3 2023
1 2022-03-01 71630 623836 446585 3 2022
2 2020-01-01 31081 172822 167797 1 2020
3 2016-04-01 16220 57039 59408 4 2016
4 2020-04-01 34686 267371 237863 4 2020
In [49]:
list(df.columns)
Out[49]:
['Trimestre',
 'Ouvert au public',
 'Particulier',
 'Société',
 'Semestre',
 'Année']
In [50]:
df.sort_values(by = ['Année', 'Semestre'], inplace = True)
df.head()
Out[50]:
Trimestre Ouvert au public Particulier Société Semestre Année
20 2015-01-01 8478 25638 34746 1 2015
10 2015-02-01 10086 29662 37933 2 2015
34 2015-03-01 10928 32278 38681 3 2015
9 2015-04-01 11113 37448 42891 4 2015
26 2016-01-01 12830 43284 48013 1 2016
In [51]:
def total(dataframe):
    dataframe['Total'] = dataframe['Ouvert au public'] + dataframe['Particulier'] + dataframe['Société']
    return dataframe

df = total(df)
df.head()
Out[51]:
Trimestre Ouvert au public Particulier Société Semestre Année Total
20 2015-01-01 8478 25638 34746 1 2015 68862
10 2015-02-01 10086 29662 37933 2 2015 77681
34 2015-03-01 10928 32278 38681 3 2015 81887
9 2015-04-01 11113 37448 42891 4 2015 91452
26 2016-01-01 12830 43284 48013 1 2016 104127
In [52]:
for col in ['Ouvert au public', 'Particulier', 'Société', 'Total']:
    plt.figure(figsize = (12,8))
    plt.plot(df['Trimestre'], df[col])
    plt.xlabel('Trimestre')
    plt.xticks(rotation=90)
    plt.ylabel('Nombre de bornes')
    plt.title(f'Évolution du nombre de recharges {col} en France')
    plt.show()

Nous avons très peu d'informations et possédons seulement des périodes. C'est pourquoi nous choisissons une simple régression linéaire.

Nous nous accordons à dire qu'avec si peu de features, les prédictions du modèle sont vraiment à prendre avec des pincettes. Toutefois, nous voulions quand même nous essayer à la prédiction dans ce projet.

In [53]:
X = df[['Semestre', 'Année']]
y = df['Total']

# Diviser les données en ensembles d'entraînement et de test
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, shuffle=False)

model = LinearRegression()
model.fit(X_train, y_train)
y_pred = model.predict(X_test)  #prédictions sur l'ensemble de test

# Évaluer les performances du modèle
mse = mean_squared_error(y_test, y_pred)
r2 = r2_score(y_test, y_pred)

# Afficher les résultats
print(f'Mean Squared Error: {mse}')
print(f'R-squared: {r2}')
Mean Squared Error: 416459288139.51404
R-squared: -2.8520673877147535
In [54]:
plt.figure(figsize=(12,8))
date_train = [year + ' Q' + sem for sem, year in X_train[['Semestre', 'Année']].values]
date_test = [year + ' Q' + sem for sem, year in X_test[['Semestre', 'Année']].values]
plt.plot(pd.to_datetime(date_train, format='%Y Q%m'), y_train, color = 'blue', label = 'Train')
plt.plot(pd.to_datetime(date_test, format='%Y Q%m'), y_test, color = 'cyan', label = 'Test')
plt.plot(pd.to_datetime(date_test, format='%Y Q%m'), y_pred, color = 'orange', label = 'Predictions')
plt.legend()
plt.xlabel('Temps')
plt.ylabel('Nombre de bornes')
plt.show()
In [55]:
# on mélange les dates d'entraînement en enlevant shuffle = False
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
model = LinearRegression()
model.fit(X_train, y_train)
y_pred = model.predict(X_test)  #prédictions sur l'ensemble de test

# Évaluer les performances du modèle
mse = mean_squared_error(y_test, y_pred)
r2 = r2_score(y_test, y_pred)

# Afficher les résultats
print(f'Mean Squared Error: {mse}')
print(f'R-squared: {r2}')
Mean Squared Error: 42673224090.80993
R-squared: 0.4712165057853327
In [56]:
X_pred = pd.DataFrame([['4', '2023'], ['1', '2024'], ['2', '2024'], ['3', '2024'], ['4', '2024'],
          ['1', '2025'], ['2', '2025'], ['3', '2025'], ['4', '2025'], 
          ['1', '2026'], ['2', '2026'], ['3', '2026'], ['4', '2026']], columns = ['Semestre', 'Année'])
date_predictions = [year + ' Q' + sem for sem, year in X_pred[['Semestre', 'Année']].values]
predictions = model.predict(X_pred)

plt.figure(figsize=(12,8))
date = [year + ' Q' + sem for sem, year in X[['Semestre', 'Année']].values]
plt.plot(pd.to_datetime(date, format='%Y Q%m'), y, color = 'blue', label = 'Connues')
plt.plot(pd.to_datetime(date_predictions, format='%Y Q%m'), predictions, color = 'orange', label = 'Predictions')
plt.legend()
plt.xlabel('Temps')
plt.ylabel('Nombre de bornes')
plt.show()

IV. Répartition des bornes électriques en France métropolitaine¶

Visualisons les bornes de recharge pour les véhicules électriques en France

Le jeu de données que nous allons utiliser date du 23 juillet 2023, vous pouvez le retrouver sur cette page.

In [57]:
# On commence par récupérer notre data set 
URL = 'https://www.data.gouv.fr/fr/datasets/r/517258d5-aee7-4fa4-ac02-bd83ede23d25'
df = pd.read_csv(URL, sep = ';')
df.sample(5)
Out[57]:
n_amenageur n_operateur n_enseigne id_station n_station ad_station code_insee xlongitude ylatitude nbre_pdc ... type_prise acces_recharge accessibilite observations date_maj source geo_point_borne code_insee_commune region departement
3142 Opel Lievin DRIVECO Opel Lievin FR*G44*PLEMPEREUROPEL62800*1 Opel Lievin Forum de l'Automobile Avenue François Mitteran... 62510.0 2.786037 50.426319 6.0 ... EF, T2 Gratuit 24/24 7/7 jours NaN 2021-03-03 https://www.data.gouv.fr/fr/datasets/liste-des... 50.426319,2.786037 62510 Hauts-de-France Pas-de-Calais
7007 Mairie de Paris Mairie de Paris Paris Recharge FR*W75*PVP*0002 2 RUE DE L'AMIRAL DE COLIGNY 2 RUE DE L'AMIRAL DE COLIGNY, 75001 PARIS 75101.0 2.340240 48.859710 6.0 ... prise T3 payant 7/7-24/24 Courant: AC mono|tarif: 120€/an|https://www.pa... 2020-07-22 https://www.data.gouv.fr/fr/datasets/paris-rec... 48.85971,2.34024 75056 Île-de-France Paris
9186 TOTAL MARKETING FRANCE TOTAL MARKETING FRANCE Belib' FR*V75*PPX08*03 Paris | Rue d'Astorg 11 66 Rue d'Astorg, 75008 Paris 75108.0 2.319389 48.872833 6.0 ... T2-EF payant 7/7-24/24 https://belib.paris 2021-05-04 https://www.data.gouv.fr/fr/datasets/belib-poi... 48.872833,2.3193886 75056 Île-de-France Paris
16501 Communauté d'Agglomération Maubeuge Val de Sambre BOUYGUES ENERGIES ET SERVICES pass pass électrique FR*H02*P59187*001 ECLAIBES - Ch Margot Ch Margot 59330 ÉCLAIBES 59187.0 3.932986 50.202984 2.0 ... EF - T2 payant 24h/24 7j/7 Recharge par badge et avec une application sma... 2020-04-03 https://www.data.gouv.fr/fr/datasets/infrastru... 50.202984,3.932986 59187 Hauts-de-France Nord
10253 Syndicat Départemental d'Énergie de Loire-Atla... Syndicat Départemental d'Énergie de Loire-Atla... SYDEGO FR*S44*P44130C Pont-Saint-Martin | Rue de la Flamme Olympique Rue de la Flamme Olympique 44860 Pont-Saint-Ma... NaN -1.540393 47.112585 1.0 ... CHAdeMO-Combo-T2 câble attaché Payant 24/24 7/7 NaN 2020-07-10 https://www.data.gouv.fr/fr/datasets/bornes-de... 47.112585,-1.540393 44130 Pays de la Loire Loire-Atlantique

5 rows × 22 columns

Catégories d'accès à la borne :

Comme l'illustre la cellule de code suivante, les catégories proposées ne sont pas très propres. Remédions à cela.

In [58]:
list(df['acces_recharge'].unique())
Out[58]:
['gratuit',
 'payant',
 'Payant',
 'Carte Mobive',
 nan,
 'Gratuit',
 'GRATUIT',
 'gratuit pour la clientèle du parking',
 '0.19€/kWh + 0.01€/min',
 'QR code / appli mobile / Carte RFID',
 'badge RFID; QR Code',
 'Payant (badge, appli et QR code)',
 'Public payant',
 'Accès payant (Badge RFID, application, site web paynow.sodetrel.fr, badges vendus en boutique)',
 'Charges gratuites de 12 à 14h et de 19h à 21h',
 'oui',
 '5€ / 45min, si abonné (10€/mois) : 1€/10min',
 'Réservation préalable, accès par badge',
 '2€/recharge',
 '2€/recharge+ prix du stationnement']
In [59]:
def transform_acces(row):
    if not pd.isna(row):  # On ne peut rien dire des nan
        row = row.lower()  # Mettre en lettre minuscule 
        mots = row.split(' ')
        if 'payant' in mots: row = 'payant'
        elif 'gratuit' in mots: row = 'gratuit'
        for mot in mots: 
            if len(mot.split('€'))>1: row = 'payant'
            if mot=='carte' or mot=='badge': row = 'carte ou badge'
            if mot=='oui': row = 'information manquante'
        #else: row = 'accès spécial'
    else: row = 'information manquante'
    return row
df['acces_recharge'] = df['acces_recharge'].apply(transform_acces)
list(df['acces_recharge'].unique())
Out[59]:
['gratuit',
 'payant',
 'carte ou badge',
 'information manquante',
 'charges gratuites de 12 à 14h et de 19h à 21h']

On veut ensuite s'assurer que nous disposons de toutes les informations concernant les coordonénes géographiques des bornes de recharge ...

In [60]:
print('Il y a ', df[df['xlongitude'].isna()].shape[0], 'valeurs manquantes pour xlongitude')
print('Il y a ', df[df['ylatitude'].isna()].shape[0], 'valeurs manquantes pour ylatitude')
sns.heatmap(df[['xlongitude', 'ylatitude']].isna(), cmap = 'magma', cbar = False)
Il y a  30 valeurs manquantes pour xlongitude
Il y a  6 valeurs manquantes pour ylatitude
Out[60]:
<Axes: >
In [61]:
droping_liste = list(set(df[df['xlongitude'].isna()].index.to_list() + df[df['ylatitude'].isna()].index.to_list()))
df.drop(droping_liste, inplace = True)

Regardons plus attentivement la répartition des bornes en France

In [62]:
bornes_region = df['region'].value_counts().reset_index()  # On compte le nombre de bornes par région
bornes_region.columns = ['region', 'nombre de bornes']
bornes_region = bornes_region.sort_values(by='nombre de bornes', ascending=False)

# On affiche cela dans un histogramme 
plt.figure(figsize=(12, 6))
plt.bar(bornes_region['region'], bornes_region['nombre de bornes'])
plt.xlabel('Région')
plt.ylabel('Nombre de bornes')
plt.title('Nombre de bornes de recharge pour les véhicules électriques par région')
plt.xticks(rotation=90)
plt.tight_layout()
plt.show()
In [63]:
# Quelques modifications pour le futur merge
bornes_region['region'][11] = 'Grand Est'
bornes_region['region'][9] = 'Île-de-France'
bornes_region['region'][8] = 'Centre-Val-de-Loire'

On peut se demander si le nombre de bornes est proportionnel au nombre de voitures électriques présentes dans ces régions.

i) Commençons par regarder l'évolution du nombre de véhicules électrique en France depuis 2010

ii) Puis regardons la répartition des nouveaux véhicules électriques en France en 2018

In [66]:
# Commençons par regarder l'évolution du nombre de véhicules électrique
# Pour cela nous avons scrappé les données de wikipedia, le contenu se trouve dans le dossier SCRAP : 'scrap_VE.py'
os.chdir("/Users/augustincablant/Documents/GitHub/Pycar")
EVOL_VE = pd.read_csv('SCRAP/EVOL_VE.csv')
fig, ax1 = plt.subplots()
ax1.plot(EVOL_VE['Année'], EVOL_VE['Part de marché'], color='red', alpha=0.8, label='Part de marché des véhicules électrique')
ax1.set_xlabel('Années')
ax1.set_ylabel('Parts de marché')
ax1.set_xlim(2010, 2023)
ax2 = ax1.twinx()
ax2.scatter(EVOL_VE['Année'], EVOL_VE['Voitures particulières'], color='blue', label='Voitures particulières')
ax2.scatter(EVOL_VE['Année'], EVOL_VE['Utilitaires'], color='orange', label='Utilitaires')
ax2.set_ylabel('Nombre de voitures')
ax1.legend()
ax2.legend(loc = 'center left')
plt.show()
In [67]:
# Répartition des nouveaux véhicules électriques en France en 2018 
VE_REP = pd.read_csv('SCRAP/VE_2018.csv')
VE_REP.sort_values(by = 'Immatriculations', ascending = False, inplace=True)
plt.figure(figsize=(12, 6))
plt.bar(VE_REP['Département'], VE_REP['Immatriculations'], color = 'grey')
plt.xlabel('Département')
plt.ylabel('Nombre de nouvelles immatriculations')
plt.title('Nombre de nouvelles immatriculations par région en France en 2018')
plt.xticks(rotation=90)
plt.tight_layout()
plt.show()
In [70]:
VE_REP = pd.read_csv('SCRAP/VE_2018.csv')
def get_number(row):
    return float('.'.join(re.findall(r'\d+', row)))
VE_REP['parts'] = VE_REP['Part de marché'].apply(get_number)

VE_REP.sort_values(by = 'parts', inplace=True, ascending = False)
plt.figure(figsize=(12, 6))
plt.bar(VE_REP['Département'], VE_REP['parts'], color = 'orange')
plt.xlabel('Département')
plt.ylabel("Part de marché de l'électrique en %")
plt.title('Part de marché des voitures électriques par région en France en 2018')
plt.xticks(rotation=90)
plt.tight_layout()
plt.show()

Que peut-on dire des villes ?

In [71]:
code_insee = pd.read_csv('https://www.insee.fr/fr/statistiques/fichier/6800675/v_commune_2023.csv')
code_insee.head()
Out[71]:
TYPECOM COM REG DEP CTCD ARR TNCC NCC NCCENR LIBELLE CAN COMPARENT
0 COM 01001 84.0 01 01D 012 5 ABERGEMENT CLEMENCIAT Abergement-Clémenciat L'Abergement-Clémenciat 0108 NaN
1 COM 01002 84.0 01 01D 011 5 ABERGEMENT DE VAREY Abergement-de-Varey L'Abergement-de-Varey 0101 NaN
2 COM 01004 84.0 01 01D 011 1 AMBERIEU EN BUGEY Ambérieu-en-Bugey Ambérieu-en-Bugey 0101 NaN
3 COM 01005 84.0 01 01D 012 1 AMBERIEUX EN DOMBES Ambérieux-en-Dombes Ambérieux-en-Dombes 0122 NaN
4 COM 01006 84.0 01 01D 011 1 AMBLEON Ambléon Ambléon 0104 NaN
In [72]:
merge_df = pd.merge(df, code_insee, left_on='code_insee_commune', right_on='COM')
In [73]:
merge_df.sample()
Out[73]:
n_amenageur n_operateur n_enseigne id_station n_station ad_station code_insee xlongitude ylatitude nbre_pdc ... REG DEP CTCD ARR TNCC NCC NCCENR LIBELLE CAN COMPARENT
729 SDEE48 48 BOUYGUES ENERGIES ET SERVICES Révéo FR*S48*P48088*001 LA MALENE - Parking Du Tarn Parking Du Tarn 48210 LA MALENE 48088.0 3.320673 44.300836 2.0 ... 76.0 48 48D 481 3 MALENE Malène La Malène 4802 NaN

1 rows × 34 columns

In [74]:
merge_df.columns
Out[74]:
Index(['n_amenageur', 'n_operateur', 'n_enseigne', 'id_station', 'n_station',
       'ad_station', 'code_insee', 'xlongitude', 'ylatitude', 'nbre_pdc',
       'id_pdc', 'puiss_max', 'type_prise', 'acces_recharge', 'accessibilite',
       'observations', 'date_maj', 'source', 'geo_point_borne',
       'code_insee_commune', 'region', 'departement', 'TYPECOM', 'COM', 'REG',
       'DEP', 'CTCD', 'ARR', 'TNCC', 'NCC', 'NCCENR', 'LIBELLE', 'CAN',
       'COMPARENT'],
      dtype='object')
In [75]:
#Il faut choisir l'échelle parmi les éléements de liste_echelle pour les fonctions suivantes

liste_echelle = ['LIBELLE','departement','region']

def top20_bornes(dataframe, echelle):
    """ 
    Renvoie un dataframe contenant les 20 plus grandes localisations selon l'échelle ayant le plus de bornes
    """
    df_villes = dataframe[echelle].value_counts()
    df_top20 = df_villes.head(20)
    return df_top20

def afficher_top20(echelle):
    """ 
    Affiche un bar chart en fonction de l'échelle donnée en argument
    """
    df = top20_bornes(merge_df,echelle)
    fig = px.bar(df)
    fig.update_layout(title_text="Les 20 {}s ayant le plus d'infrastructuress de recharge".format(echelle.lower()))
    fig.update_yaxes(title_text='Nombre de bornes')
    fig.update_xaxes(title_text='{}s'.format(echelle))
    return fig
In [76]:
afficher_top20(liste_echelle[0])
In [77]:
afficher_top20(liste_echelle[1])
In [78]:
afficher_top20(liste_echelle[2])

On peut désormais passer à la visualisation de ces bornes !

In [82]:
# Récupérer les données pour afficher la carte du monde

map_osm = folium.Map(location=[48.85, 2.34])

legend_html = """
     <div style="position: fixed; 
                 bottom: 100px; left: 50px; width: 200px; height: 120px; 
                 border:2px solid grey; z-index:9999; font-size:14px;
                 background-color:white;
                 ">
     <p style="margin:10px;">Legende</p>
     <p style="background-color:orange; margin:5px;">Payant</p>
     <p style="background-color:green; margin:5px;">Gratuit</p>
     <p style="background-color:grey; margin:5px;">Information manquante</p>
     <p style="background-color:cyan; margin:5px;">Carte ou badge</p>
     <p style="background-color:yellow; margin:5px;">Charges gratuites de 12h à 14h et de 19h à 21h</p>
      </div>
     """

# Ajoutez la légende personnalisée à la carte
map_osm.get_root().html.add_child(folium.Element(legend_html))


for index, lat, lon, com, acces_recharge in df[['ylatitude', 'xlongitude', 'n_station', 'acces_recharge']].itertuples():
    # Créer un marqueur avec une couleur différente en fonction des valeurs
    if acces_recharge == 'payant': fill_color = 'orange'
    elif acces_recharge == 'gratuit': fill_color = 'green'
    elif acces_recharge == 'information manquante': fill_color = 'grey'
    elif acces_recharge == 'carte ou badge': fill_color = 'cyan'
    elif acces_recharge == 'charges gratuites de 12 à 14h et de 19h à 21h': fill_color = 'yellow'
    # Ajouter le marqueur à la carte
    folium.RegularPolygonMarker(location=[lat, lon], popup=com, fill_color= fill_color, color =fill_color, radius=5).add_to(map_osm)

# Sauvegardez la carte au format HTML
#map_osm.save('/Users/augustincablant/Documents/GitHub/PyCar/DOWNLOAD/map_bornes.html')

map_osm
Out[82]:
Make this Notebook Trusted to load map: File -> Trust Notebook

V. Accidents France métropolitaine¶

In [10]:
os.chdir("/Users/augustincablant/Documents/GitHub/Pycar")
df_acc = pd.read_csv('SCRAP/EVOL_ACC.csv').set_index('Année')
df_acc = df_acc[1:]
df_acc = df_acc.drop(['1981 et 1982'])
df_acc['Accidents'] = df_acc['Accidents'].astype(int)
df_acc.head()
Out[10]:
Accidents
Année
1949 25247
1950 50000
1951 70000
1952 95000
1953 118881
In [11]:
plt.figure(figsize = (12, 8))
df_sampled = df_acc[::4]
plt.plot(df_acc.index, df_acc['Accidents'], marker='o', linestyle='-')
plt.title('Évolution des accidents en France métropolitaine')
plt.xlabel('Année')
plt.xticks(df_sampled.index)
plt.ylabel('Accidents')
plt.legend(['Nombre d\'accidents'], loc='upper left')
plt.show()

Accidents en Île-de-France

In [83]:
os.chdir("/Users/augustincablant/Documents/GitHub/Pycar")
accidents_2022idf_carac= pd.read_excel("notebooks/accidents/accidents_2022_idf.xlsx")

#reshape des data
accidents_2022idf_carac["Latitude"]=accidents_2022idf_carac["Latitude"].str.replace(',','.').astype(float)
accidents_2022idf_carac["Longitude"]=accidents_2022idf_carac["Longitude"].str.replace(',','.').astype(float)
accidents_2022idf_carac.columns
Out[83]:
Index(['Dépt', 'Commune', 'Date', 'Tué(s)', 'Blessés', 'dont hospitalisés',
       'dont légers', 'Milieu', 'Autoroute', 'Adresse', 'Latitude',
       'Longitude', 'Résumé'],
      dtype='object')
In [84]:
#on trie les données d'accident en fonction de leur localisation
dict_accidents_2022idf = accidents_2022idf_carac.groupby(["Commune","Adresse"]).groups
#on trie le dictionnaire obtenu
dict_trie_accidents_2022idf=dict(sorted(dict_accidents_2022idf.items(),key=lambda item : len(item[1]),reverse=True))


carte=folium.Map([48.866667,2.333333],zoom_start=12)
#on affiche les 25 localisations avec le plus d'accidents en Idf
i=0
for key in dict_trie_accidents_2022idf.keys():
    list1=[]
    for elem in dict_trie_accidents_2022idf[key]:
        list1.append(accidents_2022idf_carac.iloc[elem]["Latitude"])
        m1,m2=list1.index(min(list1)),list1.index(max(list1))
        #on affiche une ligne entre les points les plus éloignés
    coord_ligne1=[accidents_2022idf_carac.iloc[dict_trie_accidents_2022idf[key][m1]]['Longitude'],accidents_2022idf_carac.iloc[dict_trie_accidents_2022idf[key][m1]]['Latitude']]
    coord_ligne2=[accidents_2022idf_carac.iloc[dict_trie_accidents_2022idf[key][m2]]['Longitude'],accidents_2022idf_carac.iloc[dict_trie_accidents_2022idf[key][m2]]['Latitude']]
    folium.PolyLine([coord_ligne1,coord_ligne2],popup=key,weight = 0.7 * len(dict_trie_accidents_2022idf[key]/len(dict_trie_accidents_2022idf[('93066 - Saint-Denis','AUTOROUTE A1')]))).add_to(carte)
    i=i+1
    if i > 25:
        break
    
carte.save("accidents_idf_2022.html")
In [85]:
carte
Out[85]:
Make this Notebook Trusted to load map: File -> Trust Notebook